package com.tomvian.maths
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import com.tomvian.maths.Vector2D;

	/**
	 * QuadBezier
	 * @author Thomas Vian
	 */
	public class QuadBezier extends Line
	{	
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		
		public var control:Vector2D;
		
		//--------------------------------------------------------------------------
		//
		//  Getters / Setters
		//
		//--------------------------------------------------------------------------
		
		override public function get length():Number
		{
			var l:Number = 0;
			var last:Vector2D = start.clone();
			var current:Vector2D = new Vector2D();
			for(var i:Number = 1; i <= 50; i++)
			{
				this.getPoint(i / 50, current);
				l += last.subtractMod(current).length;
				last.copy(current);
			}
			return l;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		public function QuadBezier(start:Vector2D, end:Vector2D, control:Vector2D)
		{
			super(start, end);
			this.control = control;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		
		override public function getPoint(t:Number, vector2D:Vector2D = null):Vector2D
		{
			var a:Number = 1.0 - t;
			if(!vector2D) vector2D = new Vector2D();
			vector2D.x = start.x + t * (2.0 * a * (control.x - start.x) + t * (end.x - start.x));
			vector2D.y = start.y + t * (2.0 * a * (control.y - start.y) + t * (end.y - start.y));
			
			return vector2D;
		}
		
		public function getPointsArray(points:int = 10):Array
		{
			var array:Array = [];
			for(var i:int = 0; i < points; i++) array.push(this.getPoint(i / (points - 1)));
			return array;
		}
		
		public function getPointsVector(points:int = 10):Vector.<Vector2D>
		{
			var vector:Vector.<Vector2D> = new Vector.<Vector2D>();
			for(var i:int = 0; i < points; i++) vector.push(this.getPoint(i / (points - 1)));
			return vector;
		}
		
		override public function getTangentAngle(t:Number = 0.0):Number
		{
			return this.getPoint(t + 0.005).subtractMod(this.getPoint(t - 0.005)).angle;
		}
		
		override public function getTangentVector2D(t:Number = 0.0):Vector2D
		{
			return this.getPoint(t + 0.005).subtractMod(this.getPoint(t - 0.005)).normalisedMod();
		}
		
		override public function getNormal(t:Number = 0.0):Vector2D
		{
			return this.getPoint(t + 0.005).subtractMod(this.getPoint(t - 0.005)).normalMod();
		}
		
		public function getLength(divisions:Number = 50):Number
		{
			divisions = Number(int(divisions));
			var l:Number = 0;
			var last:Vector2D = start.clone();
			var current:Vector2D = new Vector2D();
			for(var i:Number = 1; i <= divisions; i++)
			{
				this.getPoint(i / divisions, current);
				l += last.subtractMod(current).length;
				last.copy(current);
			}
			return l;
		}
		
		override public function drawTo(graphics:Graphics, colour:int = 0, thickness:Number = 1.0):void
		{
			graphics.lineStyle(thickness, colour);
			graphics.moveTo(start.x, start.y);
			graphics.curveTo(control.x, control.y, end.x, end.y);
		}
		
		public function setControlFromDrawing(curve:DisplayObject):void
		{
			// Curve needs to have a parent for co-ord space reference
			if(!curve.parent) return;
			
			// Get the angle between the start and end points
			var angle:Number = (end.subtract(start).angle - Math.PI * 0.5) * 180 / Math.PI;
			
			// Rotate the curve so that start and end lay parallel to the x axis
			curve.rotation -= angle;
			var bounds:Rectangle = curve.getBounds(curve.parent);
			
			// Get the correct rotated transform for drawing
			var rotated:Matrix = curve.transform.matrix;
			rotated.translate( -bounds.x, -bounds.y);
			
			// Draw a BitmapData from the rotated curve and make sure every pixel along the curve is black
			var curveBmd:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0)
			curveBmd.draw(curve, rotated);
			curveBmd.threshold(curveBmd, curveBmd.rect, new Point(0, 0), ">", 0x00000000, 0xFF000000, 0xFFFFFFFF, true);
			
			// Rotate back to original angle
			curve.rotation += angle;
			
			// Get the true bounds of the curve
			var curveBounds:Rectangle = curveBmd.getColorBoundsRect(0xFFFFFFFF, 0xFF000000);
			curveBounds.offsetPoint(bounds.topLeft);
			
			// Find the extreme y value of the curve, either the top or bottom
			var yDiffTop:Number = start.y - curveBounds.top;
			var yDiffBottom:Number = curveBounds.bottom - start.y;
			var extremeY:Number = (yDiffTop > yDiffBottom)? curveBounds.top : curveBounds.bottom;
			
			// Draw a 1px high BitmapData along the extreme y end of the curve to get the tip of the curve as black pixels
			var lineBmpData:BitmapData = new BitmapData(curveBounds.width, 1, true, 0x00000000);
			var trans:Matrix = new Matrix();
			var moveBy:Point = curveBounds.topLeft.subtract(bounds.topLeft);
			if(extremeY > start.y) moveBy.y += curveBounds.height - 1;
			trans.translate(-moveBy.x, -moveBy.y);
			lineBmpData.draw(curveBmd, trans);
			
			// Find the bounding box of the tip pixels and convert to correct co-ord space
			var tipBounds:Rectangle = lineBmpData.getColorBoundsRect(0xFFFFFFFF, 0xFF000000);
			tipBounds.offsetPoint(curveBounds.topLeft);
			if(extremeY > start.y) tipBounds.offset(0, curveBounds.height - 1);
			
			// The center of the tip bounding box is the x position of the very tip of the curve in the y direction
			var tipPoint:Point = new Point(tipBounds.x + tipBounds.width / 2, tipBounds.y + tipBounds.height / 2);
			
			// Transform the tip point back to the un-rotated co-ords of the actual curve
			var rotateBack:Matrix = new Matrix();
			rotateBack.translate(-start.x, -start.y);
			rotateBack.rotate(angle*Math.PI / 180);
			rotateBack.translate(start.x, start.y);
			tipPoint = rotateBack.transformPoint(tipPoint);
			
			curveBmd.dispose();
			lineBmpData.dispose();
			
			// Set the control point that would produce a curve through tipPoint at t = 0.5
			control = new Vector2D();
			control.x = (2.0 * tipPoint.x) - 0.5 * (start.x + end.x);
			control.y = (2.0 * tipPoint.y) - 0.5 * (start.y + end.y);
		}
	}
}